/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under 
 * the Apache License, Version 2.0  (the "License"); you may not use this file
 * except in compliance with the License.  
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*****************************************************************************
  Source      Authentication.c

  Version     0.1

  Date        2013/03/04

  Product     NAS stack

  Subsystem   EPS Mobility Management

  Author      Frederic Maurel, Lionel GAUTHIER

  Description Defines the authentication EMM procedure executed by the
        Non-Access Stratum.

        The purpose of the EPS authentication and key agreement (AKA)
        procedure is to provide mutual authentication between the user
        and the network and to agree on a key KASME. The procedure is
        always initiated and controlled by the network. However, the
        UE can reject the EPS authentication challenge sent by the
        network.

        A partial native EPS security context is established in the
        UE and the network when an EPS authentication is successfully
        performed. The computed key material KASME is used as the
        root for the EPS integrity protection and ciphering key
        hierarchy.

*****************************************************************************/
#include <pthread.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>

#include "bstrlib.h"

#include "3gpp_24.007.h"
#include "3gpp_24.008.h"
#include "3gpp_29.274.h"
#include "common_types.h"
#include "3gpp_requirements_24.301.h"
#include "log.h"
#include "msc.h"
#include "gcc_diag.h"
#include "dynamic_memory_check.h"
#include "assertions.h"

#include "emm_data.h"
#include "emm_proc.h"
#include "emm_cause.h"
#include "emm_sap.h"
#include "nas_timer.h"

#include "mme_app_ue_context.h"
#include "nas_itti_messaging.h"
#include "mme_app_defs.h"

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/

/*
   --------------------------------------------------------------------------
    Internal data handled by the authentication procedure in the UE
   --------------------------------------------------------------------------
*/

/*
   --------------------------------------------------------------------------
    Internal data handled by the authentication procedure in the MME
   --------------------------------------------------------------------------
*/
// callbacks for authentication procedure
static void _authentication_t3460_handler (void *args);
static int _authentication_ll_failure (struct emm_data_context_s *emm_context, struct nas_emm_proc_s * emm_proc);
static int _authentication_non_delivered_ho (struct emm_data_context_s *emm_context, struct nas_emm_proc_s * emm_proc);
static int _authentication_abort (struct emm_data_context_s *emm_context, struct nas_emm_base_proc_s * emm_base_proc);

static int _start_authentication_information_procedure(struct emm_data_context_s *emm_context, nas_emm_auth_proc_t * const auth_proc, const_bstring auts);
static int _auth_info_proc_success_cb (struct emm_data_context_s *emm_ctx);
static int _auth_info_proc_failure_cb (struct emm_data_context_s *emm_ctx);


static int _authentication_check_imsi_5_4_2_5__1 (struct emm_data_context_s *emm_context);
static int _authentication_check_imsi_5_4_2_5__1_fail (struct emm_data_context_s *emm_context);
static int _authentication_request (nas_emm_auth_proc_t * auth_proc);
static int _authentication_reject (struct emm_data_context_s *emm_context, struct nas_emm_base_proc_s * emm_base_proc);

/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/


/*
   --------------------------------------------------------------------------
        Authentication procedure executed by the MME
   --------------------------------------------------------------------------
*/
/****************************************************************************
 **                                                                        **
 ** Name:    emm_proc_authentication()                                 **
 **                                                                        **
 ** Description: Initiates authentication procedure to establish partial   **
 **      native EPS security context in the UE and the MME.        **
 **                                                                        **
 **              3GPP TS 24.301, section 5.4.2.2                           **
 **      The network initiates the authentication procedure by     **
 **      sending an AUTHENTICATION REQUEST message to the UE and   **
 **      starting the timer T3460. The AUTHENTICATION REQUEST mes- **
 **      sage contains the parameters necessary to calculate the   **
 **      authentication response.                                  **
 **                                                                        **
 ** Inputs:  ue_id:      UE lower layer identifier                  **
 **      ksi:       NAS key set identifier                     **
 **      rand:      Random challenge number                    **
 **      autn:      Authentication token                       **
 **      success:   Callback function executed when the authen-**
 **             tication procedure successfully completes  **
 **      reject:    Callback function executed when the authen-**
 **             tication procedure fails or is rejected    **
 **      failure:   Callback function executed whener a lower  **
 **             layer failure occured before the authenti- **
 **             cation procedure comnpletes                **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
int
emm_proc_authentication_ksi (
  struct emm_data_context_s *emm_context,
  nas_emm_specific_proc_t  * const emm_specific_proc,
  ksi_t ksi,
  const uint8_t   * const rand,
  const uint8_t   * const autn,
  success_cb_t success,
  failure_cb_t failure)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;


  if ((emm_context) && ((EMM_DEREGISTERED == emm_context->_emm_fsm_state) || (EMM_REGISTERED == emm_context->_emm_fsm_state))){
    mme_ue_s1ap_id_t  ue_id = emm_context->ue_id;
    OAILOG_INFO (LOG_NAS_EMM, "ue_id=" MME_UE_S1AP_ID_FMT " EMM-PROC  - Initiate authentication KSI = %d\n", ue_id, ksi);

    nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_context);
    if (!auth_proc) {
      auth_proc = nas_new_authentication_procedure(emm_context);
    }

    if (auth_proc) {
//      if (emm_specific_proc) {
//        if (EMM_SPEC_PROC_TYPE_ATTACH == emm_specific_proc->type) {
//          auth_proc->is_cause_is_attach = true;
//        } else if (EMM_SPEC_PROC_TYPE_TAU == emm_specific_proc->type) {
//          auth_proc->is_cause_is_attach = false;
//        }
//      }
      auth_proc->ksi = ksi;
      if (rand) {
          memcpy (auth_proc->rand, rand, AUTH_RAND_SIZE);
      }
      // Set the authentication token
      if (autn) {
          memcpy (auth_proc->autn, autn, AUTH_AUTN_SIZE);
      }
      auth_proc->emm_cause                                     = EMM_CAUSE_SUCCESS;
      auth_proc->retransmission_count                          = 0;
      auth_proc->ue_id                                         = ue_id;
      ((nas_emm_base_proc_t *)auth_proc)->parent                   = (nas_emm_base_proc_t*)emm_specific_proc;
      auth_proc->emm_com_proc.emm_proc.delivered               = NULL;
      auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state  = emm_fsm_get_state(emm_context);
      auth_proc->emm_com_proc.emm_proc.not_delivered           = _authentication_ll_failure;
      auth_proc->emm_com_proc.emm_proc.not_delivered_ho        = _authentication_non_delivered_ho;
      auth_proc->emm_com_proc.emm_proc.base_proc.success_notif = success;
      auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif = failure;
      auth_proc->emm_com_proc.emm_proc.base_proc.abort         = _authentication_abort;
      auth_proc->emm_com_proc.emm_proc.base_proc.fail_in       = NULL; // only response
      auth_proc->emm_com_proc.emm_proc.base_proc.fail_out      = _authentication_reject;
      auth_proc->emm_com_proc.emm_proc.base_proc.time_out      = _authentication_t3460_handler;

      /*
       * Send authentication request message to the UE
       */
      rc = _authentication_request (auth_proc);

      if (rc != RETURNerror) {
        /*
         * Notify EMM that common procedure has been initiated
         */
        MSC_LOG_TX_MESSAGE (MSC_NAS_EMM_MME, MSC_NAS_EMM_MME, NULL, 0, "EMMREG_COMMON_PROC_REQ (AUTH) ue id " MME_UE_S1AP_ID_FMT " ", ue_id);
        emm_sap_t                               emm_sap = {0};

        emm_sap.primitive = EMMREG_COMMON_PROC_REQ;
        emm_sap.u.emm_reg.ue_id = ue_id;
        emm_sap.u.emm_reg.ctx   = emm_context;
        rc = emm_sap_send (&emm_sap);
      }
    }
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

//------------------------------------------------------------------------------
int
emm_proc_authentication (
  struct emm_data_context_s *emm_context,
  nas_emm_specific_proc_t  * const emm_specific_proc,
  success_cb_t success,
  failure_cb_t failure)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;

  mme_ue_s1ap_id_t                        ue_id = emm_context->ue_id;
  nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_context);
  if (!auth_proc) {
    auth_proc = nas_new_authentication_procedure(emm_context);
  }else{
    // todo: don't we need to decouple the common procedure from the specific procedure abort it first?
    // todo: in addition, when creating a specific procedure, all commons should be binded to the specific procedure to terminate them in case of an error in specific procedure collision.
  }
  if(auth_proc){

    /** We will check the parent procedure. */
    auth_proc->emm_cause                                     = EMM_CAUSE_SUCCESS;
    auth_proc->retransmission_count                          = 0;
    auth_proc->ue_id                                         = ue_id;
    /** Set the parent procedure as specific. */
    ((nas_emm_base_proc_t *)auth_proc)->parent                   = (nas_emm_base_proc_t*)emm_specific_proc;
    auth_proc->emm_com_proc.emm_proc.delivered               = NULL;
    auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state  = emm_fsm_get_state(emm_context);
    auth_proc->emm_com_proc.emm_proc.not_delivered           = NULL;
    auth_proc->emm_com_proc.emm_proc.not_delivered_ho        = NULL;
    auth_proc->emm_com_proc.emm_proc.base_proc.success_notif = success;
    auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif = failure;
    auth_proc->emm_com_proc.emm_proc.base_proc.abort         = _authentication_abort;
    auth_proc->emm_com_proc.emm_proc.base_proc.fail_in       = NULL; // only response
    auth_proc->emm_com_proc.emm_proc.base_proc.fail_out      = _authentication_reject;
    auth_proc->emm_com_proc.emm_proc.base_proc.time_out      = NULL;

    /** Checking if S6a procedure is needed. */
    bool  run_auth_info_proc = false;
    if (!IS_EMM_CTXT_VALID_AUTH_VECTORS(emm_context)) {
      // Ask upper layer to fetch new security context
      nas_auth_info_proc_t * auth_info_proc = get_nas_cn_procedure_auth_info(emm_context);
      if (!auth_info_proc) {
        auth_info_proc = nas_new_cn_auth_info_procedure(emm_context);
      }
      if (!auth_info_proc->request_sent) {
        run_auth_info_proc = true;
      }
      rc = RETURNok; /**< Will continue with the current procedure?. */
    } else {
      ksi_t                                   eksi = 0;
      if (emm_context->_security.eksi < KSI_NO_KEY_AVAILABLE) {
        REQUIREMENT_3GPP_24_301(R10_5_4_2_4__2);
        eksi = (emm_context->_security.eksi + 1) % (EKSI_MAX_VALUE + 1);
      }
      for (; eksi < MAX_EPS_AUTH_VECTORS; eksi++) {
        if (IS_EMM_CTXT_VALID_AUTH_VECTOR(emm_context, (eksi % MAX_EPS_AUTH_VECTORS))) {
          break;
        }
      }
      // eksi should always be 0
      if (!IS_EMM_CTXT_VALID_AUTH_VECTOR(emm_context, (eksi % MAX_EPS_AUTH_VECTORS))) {
        run_auth_info_proc = true;
      } else {
        rc = emm_proc_authentication_ksi (emm_context, emm_specific_proc, eksi,
            emm_context->_vector[eksi % MAX_EPS_AUTH_VECTORS].rand,
            emm_context->_vector[eksi % MAX_EPS_AUTH_VECTORS].autn,
            success, failure);
        OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
      }
    }
    if (run_auth_info_proc) {
      rc = _start_authentication_information_procedure(emm_context, auth_proc, NULL);
    }
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

//------------------------------------------------------------------------------
static int _start_authentication_information_procedure(struct emm_data_context_s *emm_context, nas_emm_auth_proc_t * const auth_proc, const_bstring auts)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  mme_ue_s1ap_id_t                        ue_id = emm_context->ue_id;
  // Ask upper layer to fetch new security context
  nas_auth_info_proc_t * auth_info_proc = get_nas_cn_procedure_auth_info(emm_context);
  if (!auth_info_proc) {
    auth_info_proc = nas_new_cn_auth_info_procedure(emm_context);
    auth_info_proc->request_sent = false;
  }

  auth_info_proc->cn_proc.base_proc.parent = &auth_proc->emm_com_proc.emm_proc.base_proc;
  auth_proc->emm_com_proc.emm_proc.base_proc.child = &auth_info_proc->cn_proc.base_proc;
  auth_info_proc->success_notif = _auth_info_proc_success_cb;
  auth_info_proc->failure_notif = _auth_info_proc_failure_cb;
  auth_info_proc->cn_proc.base_proc.time_out = s6a_auth_info_rsp_timer_expiry_handler;
  auth_info_proc->ue_id = ue_id;
  auth_info_proc->resync = auth_info_proc->request_sent;

  plmn_t visited_plmn = {0};
  visited_plmn.mcc_digit1 = emm_context->originating_tai.plmn.mcc_digit1;
  visited_plmn.mcc_digit2 = emm_context->originating_tai.plmn.mcc_digit2;
  visited_plmn.mcc_digit3 = emm_context->originating_tai.plmn.mcc_digit3;
  visited_plmn.mnc_digit1 = emm_context->originating_tai.plmn.mnc_digit1;
  visited_plmn.mnc_digit2 = emm_context->originating_tai.plmn.mnc_digit2;
  visited_plmn.mnc_digit3 = emm_context->originating_tai.plmn.mnc_digit3;

  bool is_initial_req = !(auth_info_proc->request_sent);
  auth_info_proc->request_sent = true;

  nas_start_Ts6a_auth_info (auth_info_proc->ue_id, &auth_info_proc->timer_s6a, auth_info_proc->cn_proc.base_proc.time_out, auth_info_proc->ue_id);

  nas_itti_auth_info_req (ue_id, &emm_context->_imsi, is_initial_req, &visited_plmn, MAX_EPS_AUTH_VECTORS, auts);

  OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNok);
}

//------------------------------------------------------------------------------
static int _start_authentication_information_procedure_synch(struct emm_data_context_s *emm_context, nas_emm_auth_proc_t * const auth_proc, const_bstring auts)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  // Ask upper layer to fetch new security context
  nas_auth_info_proc_t * auth_info_proc = get_nas_cn_procedure_auth_info(emm_context);

  AssertFatal( auth_info_proc == NULL, "auth_info_proc %p should have been cleared", auth_info_proc);
  if (!auth_info_proc) {
    auth_info_proc = nas_new_cn_auth_info_procedure(emm_context);
    auth_info_proc->request_sent = true;
    _start_authentication_information_procedure(emm_context, auth_proc, auts);
    OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNok);
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNerror);
}


//------------------------------------------------------------------------------
static int _auth_info_proc_success_cb (struct emm_data_context_s *emm_ctx)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  DevAssert(emm_ctx);

  nas_auth_info_proc_t * auth_info_proc = get_nas_cn_procedure_auth_info(emm_ctx);
  mme_ue_s1ap_id_t                        ue_id = emm_ctx->ue_id;
  int                                     rc = RETURNerror;

  if (auth_info_proc) {


    // compute next eksi
    ksi_t eksi = 0;
    if (emm_ctx->_security.eksi <  KSI_NO_KEY_AVAILABLE) {
      REQUIREMENT_3GPP_24_301(R10_5_4_2_4__2);
      eksi = (emm_ctx->_security.eksi + 1) % (EKSI_MAX_VALUE + 1);
    }

    /*
     * Copy provided vector to user context
     */
    for (int i = 0; i < auth_info_proc->nb_vectors; i++) {
      AssertFatal (MAX_EPS_AUTH_VECTORS > i, " TOO many vectors");
      int destination_index = (i + eksi)%MAX_EPS_AUTH_VECTORS;
      memcpy (emm_ctx->_vector[destination_index].kasme, auth_info_proc->vector[i]->kasme, AUTH_KASME_SIZE);
      memcpy (emm_ctx->_vector[destination_index].autn,  auth_info_proc->vector[i]->autn, AUTH_AUTN_SIZE);
      memcpy (emm_ctx->_vector[destination_index].rand, auth_info_proc->vector[i]->rand, AUTH_RAND_SIZE);
      memcpy (emm_ctx->_vector[destination_index].xres, auth_info_proc->vector[i]->xres.data, auth_info_proc->vector[i]->xres.size);
      emm_ctx->_vector[destination_index].xres_size = auth_info_proc->vector[i]->xres.size;
      OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Received Vector %u:\n", i);
      OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Received XRES ..: " XRES_FORMAT "\n", XRES_DISPLAY (emm_ctx->_vector[destination_index].xres));
      OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Received RAND ..: " RAND_FORMAT "\n", RAND_DISPLAY (emm_ctx->_vector[destination_index].rand));
      OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Received AUTN ..: " AUTN_FORMAT "\n", AUTN_DISPLAY (emm_ctx->_vector[destination_index].autn));
      OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Received KASME .: " KASME_FORMAT " " KASME_FORMAT "\n",
          KASME_DISPLAY_1 (emm_ctx->_vector[destination_index].kasme), KASME_DISPLAY_2 (emm_ctx->_vector[destination_index].kasme));
      emm_ctx_set_attribute_valid(emm_ctx, EMM_CTXT_MEMBER_AUTH_VECTOR0+destination_index);
    }

    nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_ctx);

    if (auth_proc) {
      if (auth_info_proc->nb_vectors > 0) {
        emm_ctx_set_attribute_present(emm_ctx, EMM_CTXT_MEMBER_AUTH_VECTORS);

        for (; eksi < MAX_EPS_AUTH_VECTORS; eksi++) {
          if (IS_EMM_CTXT_VALID_AUTH_VECTOR(emm_ctx, (eksi % MAX_EPS_AUTH_VECTORS))) {
            break;
          }
        }
        // eksi should always be 0
        ksi_t eksi_mod = eksi % MAX_EPS_AUTH_VECTORS;
        AssertFatal(IS_EMM_CTXT_VALID_AUTH_VECTOR(emm_ctx, eksi_mod), "TODO No valid vector, should not happen");


        auth_proc->ksi = eksi;

        // re-enter previous EMM state, before re-initiating the procedure
        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_ABORT;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
        emm_sap.u.emm_reg.notify    = false;
        emm_sap.u.emm_reg.free_proc = false;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);


        rc = emm_proc_authentication_ksi (emm_ctx, (nas_emm_specific_proc_t*)auth_info_proc->cn_proc.base_proc.parent, eksi,
            emm_ctx->_vector[eksi % MAX_EPS_AUTH_VECTORS].rand,
            emm_ctx->_vector[eksi % MAX_EPS_AUTH_VECTORS].autn,
            auth_proc->emm_com_proc.emm_proc.base_proc.success_notif,
            auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif);

        if (rc != RETURNok) {
          /*
           * Failed to initiate the authentication procedure
           */
          OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - " "Failed to initiate authentication procedure\n");
          auth_proc->emm_cause = EMM_CAUSE_ILLEGAL_UE;
        }
      } else {
        OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - " "Failed to initiate authentication procedure\n");
        auth_proc->emm_cause = EMM_CAUSE_ILLEGAL_UE;
        rc = RETURNerror;
      }

      nas_delete_cn_procedure(emm_ctx, &auth_info_proc->cn_proc);

      if (rc != RETURNok) {
        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
        emm_sap.u.emm_reg.notify    = true;
        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);
      }
    } else {
      nas_delete_cn_procedure(emm_ctx, &auth_info_proc->cn_proc);
    }
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}


//------------------------------------------------------------------------------
static int _auth_info_proc_failure_cb (struct emm_data_context_s *emm_ctx)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  nas_auth_info_proc_t * auth_info_proc = get_nas_cn_procedure_auth_info(emm_ctx);
  mme_ue_s1ap_id_t                        ue_id = emm_ctx->ue_id;
  int                                     rc = RETURNerror;

  if (auth_info_proc) {
    nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_ctx);

    int emm_cause = auth_info_proc->nas_cause;
    nas_delete_cn_procedure(emm_ctx, &auth_info_proc->cn_proc);
    /** Done handling the CN task, starting to handle the relevant common or specific task, in this case, the common task. */
    if (auth_proc) {
      auth_proc->emm_cause = emm_cause;

      if (EMM_COMMON_PROCEDURE_INITIATED == emm_fsm_get_state(emm_ctx)) {
        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
        emm_sap.u.emm_reg.notify    = true;
        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);
      } else {
        // cannot send sap event because in most cases EMM state is not EMM_COMMON_PROCEDURE_INITIATED
        // so use the callback of nas_emm_auth_proc_t
        // TODO seems bad design here, tricky.
        if (auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif) {
          emm_ctx->emm_cause = emm_cause;
          rc = (*auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif)(emm_ctx);
        } else {
          nas_delete_common_procedure(emm_ctx, (nas_emm_common_proc_t**)&auth_proc);
        }
      }
    }
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);

}

//------------------------------------------------------------------------------
int emm_proc_authentication_failure (
  mme_ue_s1ap_id_t ue_id,
  int emm_cause,
  const_bstring auts)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  // Get the UE context
  emm_data_context_t *emm_ctx = emm_data_context_get(&_emm_data, ue_id);
  int              rc = RETURNerror;

  if (!emm_ctx) {
    OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - Failed to authenticate the UE " MME_UE_S1AP_ID_FMT ". No EMM context is existing. \n", ue_id);
    emm_cause = EMM_CAUSE_ILLEGAL_UE;
    OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
  }

  OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Authentication failure (ue_id=" MME_UE_S1AP_ID_FMT ", cause=%d)\n", ue_id, emm_cause);
  nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_ctx);

  if (auth_proc){
    // Stop timer T3460
    REQUIREMENT_3GPP_24_301(R10_5_4_2_4__3);
    void * callback_args = NULL;
    nas_stop_T3460(emm_ctx->ue_id, &auth_proc->T3460, callback_args);


    switch (emm_cause) {
    case EMM_CAUSE_SYNCH_FAILURE:
      /*
       * USIM has detected a mismatch in SQN.
       *  Ask for a new vector.
       */
      REQUIREMENT_3GPP_24_301(R10_5_4_2_4__3);
      MSC_LOG_EVENT (MSC_NAS_EMM_MME, " SQN SYNCH_FAILURE ue id " MME_UE_S1AP_ID_FMT " ", ue_context->mme_ue_s1ap_id);

      auth_proc->sync_fail_count += 1;
      if (EMM_AUTHENTICATION_SYNC_FAILURE_MAX > auth_proc->sync_fail_count) {
        OAILOG_DEBUG (LOG_NAS_EMM, "EMM-PROC  - USIM has detected a mismatch in SQN Ask for new vector(s)\n");

        REQUIREMENT_3GPP_24_301(R10_5_4_2_7_e__3);
        // Pass back the current rand.
        REQUIREMENT_3GPP_24_301(R10_5_4_2_7_e__2);
        struct tagbstring resync_param;
        resync_param.data = (unsigned char *) calloc(1, RESYNC_PARAM_LENGTH);
        DevAssert(resync_param.data != NULL);
        if (resync_param.data == NULL) {
//          unlock_ue_contexts(ue_context);
          OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
        }

        bstring rand_bstr = blk2bstr (emm_ctx->_vector[0].rand, RAND_LENGTH_OCTETS);
        bconcat(rand_bstr, auts);
        // TODO: Double check this case as there is no identity request being sent.
        _start_authentication_information_procedure_synch(emm_ctx, auth_proc, rand_bstr);
        free_wrapper((void**)&resync_param.data);
        bdestroy_wrapper(&rand_bstr);


        emm_ctx_clear_auth_vectors(emm_ctx);
        rc = RETURNok;
//        unlock_ue_contexts(ue_context);
        OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
      } else {
        REQUIREMENT_3GPP_24_301(R10_5_4_2_7_e__NOTE3);
        auth_proc->emm_cause = EMM_CAUSE_SYNCH_FAILURE;
        emm_sap_t           emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
//        emm_sap.u.emm_reg.notify    = true;
//        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);
      }
      break;

    case EMM_CAUSE_MAC_FAILURE:
      REQUIREMENT_3GPP_24_301(R10_5_4_2_7_c__2);
      auth_proc->mac_fail_count++;
      auth_proc->sync_fail_count = 0;
      if (!IS_EMM_CTXT_PRESENT_IMSI(emm_ctx)) { // VALID means received in IDENTITY RESPONSE
        if (1 == auth_proc->mac_fail_count) {
          // Only to return to a "valid" EMM state
          {
            emm_sap_t                               emm_sap = {0};
            emm_sap.primitive = EMMREG_COMMON_PROC_ABORT;
            emm_sap.u.emm_reg.ue_id     = ue_id;
            emm_sap.u.emm_reg.ctx       = emm_ctx;
            emm_sap.u.emm_reg.notify    = false;
            emm_sap.u.emm_reg.free_proc = false;
            emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
            emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
            rc = emm_sap_send (&emm_sap);
          }
          rc = emm_proc_identification (emm_ctx, &auth_proc->emm_com_proc.emm_proc, IDENTITY_TYPE_2_IMSI, _authentication_check_imsi_5_4_2_5__1, _authentication_check_imsi_5_4_2_5__1_fail);
        } else {
          rc = RETURNerror;
        }

        if (rc != RETURNok) {
          REQUIREMENT_3GPP_24_301(R10_5_4_2_7_c__NOTE1); // more or less this case...
          // Failed to initiate the identification procedure
          auth_proc->emm_cause = EMM_CAUSE_MAC_FAILURE;// EMM_CAUSE_ILLEGAL_UE;
          /*
           * Notify EMM that the authentication procedure failed
           */
          emm_sap_t           emm_sap = {0};
          emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
          emm_sap.u.emm_reg.ue_id     = ue_id;
          emm_sap.u.emm_reg.ctx       = emm_ctx;
//          emm_sap.u.emm_reg.notify    = true;
//          emm_sap.u.emm_reg.free_proc = true;
          emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
          emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
          rc = emm_sap_send (&emm_sap);
        }
      } else {
        REQUIREMENT_3GPP_24_301(R10_5_4_2_5__2);
        auth_proc->emm_cause = EMM_CAUSE_MAC_FAILURE; //EMM_CAUSE_ILLEGAL_UE;
        // Do not accept the UE to attach to the network
        emm_sap_t           emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
//        emm_sap.u.emm_reg.notify    = true;
//        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);
      }
      break;

    case EMM_CAUSE_NON_EPS_AUTH_UNACCEPTABLE:
      // never happened TODO check the code
      auth_proc->sync_fail_count = 0;
      REQUIREMENT_3GPP_24_301(R10_5_4_2_7_d__1);
      // test IS_EMM_CTXT_VALID_IMSI should be enough...
      if ((emm_ctx->is_initial_identity_imsi) || (IS_EMM_CTXT_VALID_IMSI(emm_ctx))) {
        rc = RETURNerror;
      } else {
        // Only to return to a "valid" EMM state
        {
          emm_sap_t                               emm_sap = {0};
          emm_sap.primitive = EMMREG_COMMON_PROC_ABORT;
          emm_sap.u.emm_reg.ue_id     = ue_id;
          emm_sap.u.emm_reg.ctx       = emm_ctx;
          emm_sap.u.emm_reg.notify    = false;
          emm_sap.u.emm_reg.free_proc = false;
          emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
          emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
          rc = emm_sap_send (&emm_sap);
        }
        if (auth_proc->unchecked_imsi) {
          free_wrapper((void**)&auth_proc->unchecked_imsi);
        }
        auth_proc->unchecked_imsi = calloc(1, sizeof(*auth_proc->unchecked_imsi));
        memcpy(auth_proc->unchecked_imsi, &emm_ctx->_imsi, sizeof(*auth_proc->unchecked_imsi));
        rc = emm_proc_identification (emm_ctx, &auth_proc->emm_com_proc.emm_proc, IDENTITY_TYPE_2_IMSI, _authentication_check_imsi_5_4_2_5__1, _authentication_check_imsi_5_4_2_5__1_fail);
      }
      if (rc != RETURNok) {
        REQUIREMENT_3GPP_24_301(R10_5_4_2_7_d__NOTE2); // more or less this case...
        // Failed to initiate the identification procedure
        OAILOG_WARNING (LOG_NAS_EMM, "ue_id=" MME_UE_S1AP_ID_FMT "EMM-PROC  - Failed to initiate identification procedure\n", emm_ctx->ue_id);
        auth_proc->emm_cause = EMM_CAUSE_ILLEGAL_UE;
        // Do not accept the UE to attach to the network
        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
        emm_sap.u.emm_reg.ue_id     = ue_id;
        emm_sap.u.emm_reg.ctx       = emm_ctx;
//        emm_sap.u.emm_reg.notify    = true;
//        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);
      }
      break;

    default:
      auth_proc->sync_fail_count = 0;
      OAILOG_DEBUG (LOG_NAS_EMM, "EMM-PROC  - The MME received an unknown EMM CAUSE %d\n", emm_cause);
//      unlock_ue_contexts(ue_context);
      OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
    }
  }
//  unlock_ue_contexts(ue_context);
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}


/****************************************************************************
 **                                                                        **
 ** Name:    emm_proc_authentication_complete()                            **
 **                                                                        **
 ** Description: Performs the authentication completion procedure executed **
 **      by the network.                                                   **
 **                                                                        **
 **              3GPP TS 24.301, section 5.4.2.4                           **
 **      Upon receiving the AUTHENTICATION RESPONSE message, the           **
 **      MME shall stop timer T3460 and check the correctness of           **
 **      the RES parameter.                                                **
 **                                                                        **
 ** Inputs:  ue_id:      UE lower layer identifier                          **
 **      emm_cause: Authentication failure EMM cause code                  **
 **      res:       Authentication response parameter. or auts             **
 **                 in case of sync failure                                **
 **      Others:    None                                                   **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                                  **
 **      Others:    _emm_data, T3460                                       **
 **                                                                        **
 ***************************************************************************/
int
emm_proc_authentication_complete (
  mme_ue_s1ap_id_t ue_id,
  int emm_cause,
  const_bstring const res)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;
  OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Authentication complete (ue_id=" MME_UE_S1AP_ID_FMT ")\n", ue_id);
  // Get the UE context
  emm_data_context_t *emm_ctx = emm_data_context_get(&_emm_data, ue_id);

  if (!emm_ctx) {
    OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - Failed to authenticate the UE due to NULL emm_data_context_t. \n");
    OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
  }

  OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Authentication complete (ue_id=" MME_UE_S1AP_ID_FMT ", cause=%d)\n", ue_id, emm_cause);
  nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_ctx);

  if (auth_proc){

    // Stop timer T3460
    REQUIREMENT_3GPP_24_301(R10_5_4_2_4__1);

    void * callback_arg = NULL;
    nas_stop_T3460(ue_id, &auth_proc->T3460, callback_arg); /**< Stop the timer, leave the procedure?. */

    // todo: check RES!

    REQUIREMENT_3GPP_24_301(R10_5_4_2_4__2);
    emm_ctx_set_security_eksi(emm_ctx, auth_proc->ksi);
    OAILOG_DEBUG (LOG_NAS_EMM, "EMM-PROC  - Success to authentify the UE  RESP XRES == XRES UE CONTEXT\n");
    /*
     * Notify EMM that the authentication procedure successfully completed
     */
    emm_sap_t                               emm_sap = {0};
    emm_sap.primitive = EMMREG_COMMON_PROC_CNF;
    emm_sap.u.emm_reg.ue_id = ue_id;
    emm_sap.u.emm_reg.ctx = emm_ctx;
    emm_sap.u.emm_reg.notify    = true;
    emm_sap.u.emm_reg.free_proc = true;
    emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
    emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
    rc = emm_sap_send (&emm_sap);
  }
//  unlock_ue_contexts(ue_context);
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/


/*
   --------------------------------------------------------------------------
                Timer handlers
   --------------------------------------------------------------------------
*/

/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_t3460_handler()                           **
 **                                                                        **
 ** Description: T3460 timeout handler                                     **
 **      Upon T3460 timer expiration, the authentication request   **
 **      message is retransmitted and the timer restarted. When    **
 **      retransmission counter is exceed, the MME shall abort the **
 **      authentication procedure and any ongoing EMM specific     **
 **      procedure and release the NAS signalling connection.      **
 **                                                                        **
 **              3GPP TS 24.301, section 5.4.2.7, case b                   **
 **                                                                        **
 ** Inputs:  args:      handler parameters                         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    None                                       **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
static void  _authentication_t3460_handler (void *args)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  emm_data_context_t                       *emm_ctx = (emm_data_context_t *) (args);

  if (!(emm_ctx)) {
    OAILOG_ERROR (LOG_NAS_EMM, "T3460 timer expired No EMM context\n");
    OAILOG_FUNC_OUT (LOG_NAS_EMM);
  }
  nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_ctx);

  mme_ue_s1ap_id_t ue_id = emm_ctx->ue_id;

  if (auth_proc){
    /*
     * Increment the retransmission counter
     */
    REQUIREMENT_3GPP_24_301(R10_5_4_2_7_b);
    // TODO the network shall abort any ongoing EMM specific procedure.

    auth_proc->retransmission_count += 1;
    OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - T3460 timer expired, retransmission " "counter = %d\n", auth_proc->retransmission_count);

    if (auth_proc->retransmission_count < AUTHENTICATION_COUNTER_MAX) {
      /*
       * Send authentication request message to the UE
       */
      _authentication_request (auth_proc);
    } else {
      /*
       * Abort the authentication procedure
       */
      emm_sap_t                               emm_sap = {0};
      emm_sap.primitive = EMMREG_COMMON_PROC_ABORT;
      emm_sap.u.emm_reg.ue_id     = auth_proc->ue_id;
      emm_sap.u.emm_reg.ctx       = emm_ctx;
      emm_sap.u.emm_reg.notify    = true;
      emm_sap.u.emm_reg.free_proc = true;
      emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
      emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
      emm_sap_send (&emm_sap);

      /*
       * Check if the EMM context is removed removed.
       * A non delivery indicator would just retrigger the message, not a guarantee for removal.
       */
      emm_ctx = emm_data_context_get(&_emm_data, auth_proc->ue_id);
      if(emm_ctx){
        OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - EMM Context for ueId " MME_UE_S1AP_ID_FMT " is still existing. Removing failed EMM context.. \n", auth_proc->ue_id);
        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMCN_IMPLICIT_DETACH_UE;
        emm_sap.u.emm_cn.u.emm_cn_implicit_detach.ue_id = auth_proc->ue_id;
        emm_sap_send (&emm_sap);
        OAILOG_FUNC_OUT (LOG_NAS_EMM);
      }else{
        OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - EMM Context for ueId " MME_UE_S1AP_ID_FMT " is not existing. Triggering an MME_APP detach.. \n", auth_proc->ue_id);
        nas_itti_esm_detach_ind(ue_id, false);
        OAILOG_FUNC_OUT (LOG_NAS_EMM);
      }
    }
  }else{
    OAILOG_WARNING (LOG_NAS_EMM, "EMM-PROC  - T3460 timer expired, but no authentication procedure exists for ueId " MME_UE_S1AP_ID_FMT ". Ignoring. \n", ue_id);
  }
  OAILOG_FUNC_OUT (LOG_NAS_EMM);
}

/*
   --------------------------------------------------------------------------
                MME specific local functions
   --------------------------------------------------------------------------
*/

static int _authentication_check_imsi_5_4_2_5__1 (struct emm_data_context_s *emm_context)
{
  int                                  rc = RETURNerror;

   if (!(emm_context)) {
     OAILOG_ERROR (LOG_NAS_EMM, "T3460 timer expired No EMM context\n");
     OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNerror);
   }
   nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_context);

   if (auth_proc){
    REQUIREMENT_3GPP_24_301(R10_5_4_2_5__1);
    if (IS_EMM_CTXT_VALID_IMSI(emm_context)) { // VALID means received in IDENTITY RESPONSE
      // if IMSI are not equal
      if (memcmp(auth_proc->unchecked_imsi, &emm_context->_imsi, sizeof(imsi_t))) {
        // the authentication should be restarted with the correct parameters

        emm_ctx_clear_auth_vectors(emm_context);

        success_cb_t success_cb = auth_proc->emm_com_proc.emm_proc.base_proc.success_notif;
        failure_cb_t failure_cb = auth_proc->emm_com_proc.emm_proc.base_proc.failure_notif;
        nas_emm_specific_proc_t  * emm_specific_proc = (nas_emm_specific_proc_t*)((nas_emm_base_proc_t *)auth_proc)->parent;

        emm_sap_t                               emm_sap = {0};
        emm_sap.primitive = EMMREG_COMMON_PROC_ABORT;
        emm_sap.u.emm_reg.ue_id     = auth_proc->ue_id;
        emm_sap.u.emm_reg.ctx       = emm_context;
        emm_sap.u.emm_reg.notify    = true;
        emm_sap.u.emm_reg.free_proc = true;
        emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
        emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
        rc = emm_sap_send (&emm_sap);

        rc = emm_proc_authentication (emm_context, emm_specific_proc, success_cb, failure_cb);
        OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
      }
    }
    REQUIREMENT_3GPP_24_301(R10_5_4_2_5__2);
    emm_sap_t                               emm_sap = {0};
    emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
    emm_sap.u.emm_reg.ue_id     = auth_proc->ue_id;
    emm_sap.u.emm_reg.ctx       = emm_context;
    emm_sap.u.emm_reg.notify    = true;
    emm_sap.u.emm_reg.free_proc = true;
    emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
    emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
    rc = emm_sap_send (&emm_sap);
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

//------------------------------------------------------------------------------
static int _authentication_check_imsi_5_4_2_5__1_fail (struct emm_data_context_s *emm_context)
{
  int                                  rc = RETURNerror;
  if (!(emm_context)) {
    OAILOG_ERROR (LOG_NAS_EMM, "T3460 timer expired No EMM context\n");
    OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNerror);
  }
  nas_emm_auth_proc_t * auth_proc = get_nas_common_procedure_authentication(emm_context);

  if (auth_proc){
    REQUIREMENT_3GPP_24_301(R10_5_4_2_5__2);
    emm_sap_t                               emm_sap = {0};
    emm_sap.primitive = EMMREG_COMMON_PROC_REJ;
    emm_sap.u.emm_reg.ue_id     = auth_proc->ue_id;
    emm_sap.u.emm_reg.ctx       = emm_context;
    emm_sap.u.emm_reg.notify    = true;
    emm_sap.u.emm_reg.free_proc = true;
    emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
    emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
    rc = emm_sap_send (&emm_sap);
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_request()                                 **
 **                                                                        **
 ** Description: Sends AUTHENTICATION REQUEST message and start timer T3460**
 **                                                                        **
 ** Inputs:  args:      handler parameters                         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    T3460                                      **
 **                                                                        **
 ***************************************************************************/
static int _authentication_request (nas_emm_auth_proc_t * auth_proc)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;
  struct emm_data_context_s              *emm_ctx_p= NULL;

  if (auth_proc) {
    /*
     * Notify EMM-AS SAP that Authentication Request message has to be sent
     * to the UE
     */
    emm_sap_t                               emm_sap = {0};
    emm_sap.primitive = EMMAS_SECURITY_REQ;
    emm_sap.u.emm_as.u.security.puid = auth_proc->emm_com_proc.emm_proc.base_proc.nas_puid;
    emm_sap.u.emm_as.u.security.guti = NULL;
    emm_sap.u.emm_as.u.security.ue_id = auth_proc->ue_id;
    emm_sap.u.emm_as.u.security.msg_type = EMM_AS_MSG_TYPE_AUTH;
    emm_sap.u.emm_as.u.security.ksi = auth_proc->ksi;
    memcpy(emm_sap.u.emm_as.u.security.rand, auth_proc->rand, AUTH_RAND_SIZE);
    memcpy(emm_sap.u.emm_as.u.security.autn, auth_proc->autn, AUTH_AUTN_SIZE);
    /*
     * TODO: check for pointer validity
     */
    emm_ctx_p= emm_data_context_get(&_emm_data, auth_proc->ue_id);
    if (!emm_ctx_p) {
      OAILOG_ERROR (LOG_NAS_EMM, "No EMM context could be found for ueId " MME_UE_S1AP_ID_FMT ". "
          "Cannot proceed with identification procedure. \n", auth_proc->ue_id);
      OAILOG_FUNC_RETURN (LOG_NAS_EMM, RETURNerror);
    }
    /*
     * Setup EPS NAS security data
     */
    emm_as_set_security_data (&emm_sap.u.emm_as.u.security.sctx, &emm_ctx_p->_security, false, true);
    REQUIREMENT_3GPP_24_301(R10_5_4_2_2);
    MSC_LOG_TX_MESSAGE (MSC_NAS_EMM_MME, MSC_NAS_EMM_MME, NULL, 0, "EMMAS_SECURITY_REQ ue id " MME_UE_S1AP_ID_FMT " ", auth_proc->ue_id);
    rc = emm_sap_send (&emm_sap);

    if (rc != RETURNerror) {
      if (emm_ctx_p) {
        if (auth_proc->T3460.id != NAS_TIMER_INACTIVE_ID) { /**< NAS timer will be restarted after sending S6a. */
          void * timer_callback_args = NULL;
          nas_stop_T3460(auth_proc->ue_id, &auth_proc->T3460, timer_callback_args);
        }
        /*
         * Start T3460 timer
         */
        nas_start_T3460(auth_proc->ue_id, &auth_proc->T3460, auth_proc->emm_com_proc.emm_proc.base_proc.time_out, (void*)emm_ctx_p);
      }
    }
//    unlock_ue_contexts(ue_context);
  }

  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_reject()                                  **
 **                                                                        **
 ** Description: Sends AUTHENTICATION REJECT message                       **
 **                                                                        **
 ** Inputs:  ue_id:      UE lower layer identifier                  **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
static int _authentication_reject (emm_data_context_t *emm_context, struct nas_emm_base_proc_s * emm_base_proc)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  emm_sap_t                               emm_sap = {0};
  int                                     rc = RETURNerror;
  if ((emm_base_proc) && (emm_context)) {
    nas_emm_auth_proc_t * auth_proc = (nas_emm_auth_proc_t *)emm_base_proc;


    /*
     * Notify EMM-AS SAP that Authentication Reject message has to be sent
     * to the UE
     */
    emm_sap.primitive                    = EMMAS_SECURITY_REJ;
    emm_sap.u.emm_as.u.security.guti     = NULL;
    emm_sap.u.emm_as.u.security.ue_id    = auth_proc->ue_id;
    emm_sap.u.emm_as.u.security.msg_type = EMM_AS_MSG_TYPE_AUTH;

    /*
     * Setup EPS NAS security data
     */
    emm_as_set_security_data (&emm_sap.u.emm_as.u.security.sctx, &emm_context->_security, false, true);
    rc = emm_sap_send (&emm_sap);

  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_ll_failure()                                   **
 **                                                                        **
 ** Description: Aborts the authentication procedure currently in progress **
 ** Inputs:  args:      Authentication data to be released         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    T3460                                      **
 **                                                                        **
 ***************************************************************************/
static int _authentication_ll_failure (struct emm_data_context_s *emm_context, struct nas_emm_proc_s * emm_proc)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;
  if ((emm_proc) && (emm_context)) {
    REQUIREMENT_3GPP_24_301(R10_5_4_2_7_a);
    nas_emm_auth_proc_t * auth_proc = (nas_emm_auth_proc_t *)emm_proc;
    emm_sap_t                               emm_sap = {0};

    MSC_LOG_TX_MESSAGE (MSC_NAS_EMM_MME, MSC_NAS_EMM_MME, NULL, 0, "0 EMMREG_PROC_ABORT ue id " MME_UE_S1AP_ID_FMT " ", auth_proc->ue_id);
    emm_sap.primitive           = EMMREG_COMMON_PROC_ABORT;
    emm_sap.u.emm_reg.ue_id     = auth_proc->ue_id;
    emm_sap.u.emm_reg.ctx       = emm_context;
    emm_sap.u.emm_reg.notify    = true;
    emm_sap.u.emm_reg.free_proc = true;
    emm_sap.u.emm_reg.u.common.common_proc = &auth_proc->emm_com_proc;
    emm_sap.u.emm_reg.u.common.previous_emm_fsm_state = auth_proc->emm_com_proc.emm_proc.previous_emm_fsm_state;
    rc = emm_sap_send (&emm_sap);
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_non_delivered()                                   **
 **                                                                        **
 ** Description:  **
 ** Inputs:  args:      Authentication data to be released         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    T3460                                      **
 **                                                                        **
 ***************************************************************************/
static int _authentication_non_delivered_ho (struct emm_data_context_s *emm_context, struct nas_emm_proc_s * emm_proc)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;
  if ((emm_proc) && (emm_context)) {
    nas_emm_auth_proc_t * auth_proc = (nas_emm_auth_proc_t *)emm_proc;
    REQUIREMENT_3GPP_24_301(R10_5_4_2_7_j);
    rc = _authentication_request(auth_proc);
  }
  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}


/****************************************************************************
 **                                                                        **
 ** Name:    _authentication_abort()                                   **
 **                                                                        **
 ** Description: Aborts the authentication procedure currently in progress **
 **                                                                        **
 ** Inputs:  args:      Authentication data to be released         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    T3460                                      **
 **                                                                        **
 ***************************************************************************/
static int _authentication_abort (emm_data_context_t *emm_ctx, struct nas_emm_base_proc_s * emm_base_proc)
{
  OAILOG_FUNC_IN (LOG_NAS_EMM);
  int                                     rc = RETURNerror;

  if ((emm_base_proc) && (emm_ctx)) {
    nas_emm_auth_proc_t * auth_proc = (nas_emm_auth_proc_t *)emm_base_proc;
    OAILOG_INFO (LOG_NAS_EMM, "EMM-PROC  - Abort authentication procedure " "(ue_id=" MME_UE_S1AP_ID_FMT ")\n", emm_ctx->ue_id);

    /*
     * Stop timer T3460
     */
    void * timer_callback_args = NULL;
    nas_stop_T3460(emm_ctx->ue_id, &auth_proc->T3460, timer_callback_args);
  }

  OAILOG_FUNC_RETURN (LOG_NAS_EMM, rc);
}
